#include "Camera.hpp"

Camera::Camera ()
{
    position = Vec3d(0.0,0.0,0.0);
    orientation = Axes( Vec3d(1.0, 0.0, 0.0),
                        Vec3d(0.0, 1.0, 0.0),
                        Vec3d(0.0, 0.0, 1.0) );
    CommonInit();
}

Camera::Camera (Vec3d nPosition, Axes nOrientation)
{
    position = nPosition;
    orientation = nOrientation;

    CommonInit();
}


/* places Camera in environment */
void Camera::Place() 
{
    Vec3d lookAtPos = position + GetLookAtDirection();
    Vec3d upVec = GetUpDirection();
    gluLookAt(position.x, position.y, position.z,
              lookAtPos.x, lookAtPos.y, lookAtPos.z, 
              upVec.x, upVec.y, upVec.z); // approximate up vector
}

void Camera::SetFoV (double newFoV)
{    FoV = newFoV;    }

void Camera::SetRotationSensitivity (double newSens)
{    rotationSensitivity = newSens;    }

void Camera::SetMovementSpeed (double newMoveSpeed)
{
    movementSpeed = newMoveSpeed;
}

double Camera::GetFoV ()
{    return FoV;    }

double Camera::GetAspect()
{    return aspect;    }

double Camera::GetNearPlane()
{    return nearPlane;    }

double Camera::GetFarPlane()
{    return farPlane;    }

void Camera::UpdateAspect (int w, int h)
{
    aspect = (float)w/(float)h;
}

double Camera::GetMovementSpeed()
{   return movementSpeed;   }

double Camera::GetOrbitDistance()
{   return orbitDistance;   }

Vec3d Camera::GetLookAtDirection()
{
    return orientation.getGlobalPointFromLocalPoint(lookAtDirection).normalized();
}

Vec3d Camera::GetPosition()
{
    return position;
}

Vec3d Camera::GetForwardDirection()
{
    return orientation.getAxis(AXES_X_AXIS);
}

Vec3d Camera::GetUpDirection()
{
    return orientation.getAxis(AXES_Y_AXIS);
}

Axes Camera::GetOrientation()
{
    return orientation;
}

void Camera::SetOrientation(Axes newOrientation)
{
    orientation = newOrientation;
}

bool Camera::RotateHorizontal (double angle)
{
    // this rotates the whole AXIS
    orientation.rotateAxesAroundLocalAxis(AXES_Y_AXIS, angle*rotationSensitivity);
    return true;
} 

bool Camera::RotateVertical (double angle)
{
    angle = -angle; //bah, inverted as it's now, this should fix it fine!
    // this only rotates the look-at vector
    // +angle = rotate up, -angle = rotate down
    Vec3d tRes = orientation.rotatePointAroundGlobalAxis(lookAtDirection, AXES_Z_AXIS, angle*rotationSensitivity);
    
    if (tRes.x < 0.0001)
    {   // we've gone too far!
        return false;
    }
    lookAtDirection = tRes;
    return true;
}

/*

void Camera::SetModeTo(int CAMERA_MODE_ )
{
    if (CAMERA_MODE_ == CAMERA_MODE_FREEFLY)
    {
        mode = CAMERA_MODE_FREEFLY;
    }
    else if (CAMERA_MODE_ == CAMERA_MODE_ORBIT && orbitTarget != NULL)
    {   
        mode = CAMERA_MODE_ORBIT;
    }
}

void Camera::SetOrbitTarget (Vec3d *newTarget)
{
    orbitTarget = newTarget;
}

void Camera::SetOrbitDistance (double dist)
{
    orbitDistance = std::min(std::max(dist, orbitDistanceMin), orbitDistanceMax);
}

void Camera::AdjustOrbitdisanceBy(double d_dist)
{
    orbitAdjustment += d_dist;
}

*/

void Camera::SetMovingForward(bool tf)
{
    isMovingForward = tf;
}
void Camera::SetMovingBackward (bool tf)
{
    isMovingBackward = tf;
}
void Camera::SetMovingLeft (bool tf)
{
    isMovingLeft = tf;
}
void Camera::SetMovingRight(bool tf)
{
    isMovingRight = tf;
}
void Camera::SetMovingUp(bool tf)
{
    isMovingUp = tf;
}
void Camera::SetMovingDown (bool tf)
{
    isMovingDown = tf;
}

void Camera::MoveTo (Vec3d newPosition, Axes newOrientation)
{
    position = newPosition;
    orientation = newOrientation;
}

void Camera::Update(double dTime)
{
    if (isMovingForward)
    {
        position = position + GetLookAtDirection()*dTime;
    }
    else if (isMovingBackward)
    {
        position = position - GetLookAtDirection()*dTime;
    }

    if (isMovingRight)
    {
        position = position + orientation.getAxis(AXES_Z_AXIS)*dTime;
    }
    else if (isMovingLeft)
    {
        position = position - orientation.getAxis(AXES_Z_AXIS)*dTime;
    }

    if (isMovingUp)
    {
         position = position + orientation.getAxis(AXES_Y_AXIS)*dTime;
    }
    else if (isMovingDown)
    {
         position = position - orientation.getAxis(AXES_Y_AXIS)*dTime;
    }
}

Camera* Camera::GetInstance()
{
    if (instancePointer != NULL)
        return instancePointer;
    else
        throw "FATAL ERROR: CAMERA SINGLETON TRIED TO BE ACCESSED BEFORE IT'S INITIALIZED!";
}

// private

Camera* Camera::instancePointer = NULL;

void Camera::CommonInit()
{
    instancePointer = this;

    FoV = GLOBAL_FOV;
    aspect = (float)App::I().GetWindow().getSize().x/App::I().GetWindow().getSize().y;
    farPlane = GLOBAL_FAR_PLANE;
    nearPlane = GLOBAL_NEAR_PLANE;
    rotationSensitivity = 0.15f;

    mode = CAMERA_MODE_FREEFLY; // irrelevant for now

    orbitTarget = NULL;
    orbitDistance = 10.0f;
    orbitDistanceMin = 1.5f;
    orbitDistanceMax = 20.0f;
    orbitHorAngle = 0.0f;
    orbitVerAngle = 45.0f;
    orbitAdjustment = 0.0f;

    isMovingForward = false;
    isMovingBackward = false;
    isMovingLeft = false;
    isMovingRight = false;
    isMovingUp = false;
    isMovingDown = false;
    rotated = false;

    movementSpeed = 1;
    lookAtDirection = Vec3d(1,0,0); // look along the x-axis
}
